/** 
* All Rights Reserved , Copyright (C) 2021 , 青岛鼎信通讯股份有限公司
* 
* FileBlockRecordService
* 文件分块
* 
* 修改纪录
* 2021-03-13 版本：1.0 donghang 创建。
* @version 版本：1.0
* @author 作者：donghang
* 创建日期：2021-03-13
*/
package com.topscomm.center.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.topscomm.basic.exception.ServiceException;
import com.topscomm.center.pojo.FileBlockRecordEntity;
import com.topscomm.center.service.IFileBlockRecordService;
import com.topscomm.center.service.impl.auto.FileBlockRecordServiceAuto;
import com.topscomm.core.factory.FileStoreFactoryService;
import com.topscomm.file.FileInfoDTO;
import com.topscomm.file.FileProperties;
import com.topscomm.file.convert.ffmpeg.FfmpegFileVideoConvert;
import com.topscomm.file.util.Utils;
import com.topscomm.pub.util.ConvertUtil;
import com.topscomm.pub.util.StringBufferProxy;

import lombok.extern.slf4j.Slf4j;

@Service(value = "fileBlockRecordService")
@Scope(value = "singleton")
@Slf4j
public class FileBlockRecordService extends FileBlockRecordServiceAuto implements IFileBlockRecordService {
	@Autowired
	private FileStoreFactoryService fileStoreFactoryService;
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	/**
	 * 存储路径
	 */
	@Autowired
	private FileProperties fileProperties;
	@Autowired
	private FileManageService fileManageService;

	/**
	 * 文件块上传，同一个文件需要需要加锁，避免同时传一个文件块出现问题
	 *
	 * @description:
	 * @param
	 * @param blockEntity
	 * @author: donghang
	 * @date: 2020年8月17日上午9:23:35
	 * @modify:
	 */
	@Transactional(rollbackFor = Exception.class)
	public void uploadFileBlock(InputStream inputStream, Map<String, Object> dataMap) {
		try {
			// 数据库保存文件块，首先判断该文件是否已经上传
			StringBufferProxy sql = new StringBufferProxy();
			sql.appendSingle(" identifier='{0}' and blocknum='{1}'",
					ConvertUtil.convertToString(dataMap.get("identifier")),
					ConvertUtil.convertToString(dataMap.get("blocknum")));
			// 不存在的文件块，则插入
			if (this.queryMapByWhere(sql.toString()).size() == 0) {
				String fileName = ConvertUtil.convertToString(dataMap.get("filename"));
				StringBufferProxy newFileName = new StringBufferProxy();
				newFileName.appendSingle("{0}.{1}", ConvertUtil.convertToString(dataMap.get("identifier")),
						Utils.getExtension(fileName));
				// 构建文件信息,调用文件服务器上传
				FileInfoDTO fileInfoDTO = new FileInfoDTO();
				fileInfoDTO.setFileDirPath(ConvertUtil.convertToString(dataMap.get("filedirpath")));
				fileInfoDTO.setFileName(newFileName.toString());
				fileInfoDTO.setFileSize(ConvertUtil.convertToLong(dataMap.get("filesize")));
				fileInfoDTO.setBlockSize(ConvertUtil.convertToLong(dataMap.get("blocksize")));
				fileInfoDTO.setBucketName(ConvertUtil.convertToString(dataMap.get("bucketname")));
				fileInfoDTO.setFileId(ConvertUtil.convertToString(dataMap.get("identifier")));
				fileInfoDTO.setTotalBlockCount(ConvertUtil.convertToInt(dataMap.get("totalblockcount")));
				// 这里并发需要控制，加锁实现
				Object presignedObject = this.redisTemplate.opsForValue()
						.get(ConvertUtil.convertToString(dataMap.get("identifier")));
				// 得到预签名的
				if (presignedObject == null) {
					presignedObject = this.fileStoreFactoryService.initMultiPartUpload(fileInfoDTO);
					// 设置过期时间，比minio域签名时间要短，minio 7天有效
					this.redisTemplate.opsForValue().set(ConvertUtil.convertToString(dataMap.get("identifier")),
							presignedObject, 6, TimeUnit.DAYS);
				}
				Map<String, Object> presignedMap = (Map<String, Object>) presignedObject;
				Map<String, String> uploadUrlsMap = (Map<String, String>) presignedMap.get("uploadUrlsMap");
				this.fileStoreFactoryService.uploadPresigned(inputStream,
						uploadUrlsMap.get(ConvertUtil.convertToString(dataMap.get("blocknum"))));
				// 更新记录
				FileBlockRecordEntity fileBlockRecordEntity = new FileBlockRecordEntity();
				fileBlockRecordEntity = (FileBlockRecordEntity) fileBlockRecordEntity.convertFromMap(dataMap);
				this.insert(fileBlockRecordEntity);
			}
		} catch (Exception e) {
			log.error("接收块异常:" + ConvertUtil.convertToString(dataMap.get("identifier")) + "-"
					+ ConvertUtil.convertToString(dataMap.get("sourceid"))
					+ ConvertUtil.convertToString(dataMap.get("sourcetype"))
					+ ConvertUtil.convertToString(dataMap.get("blocknum")) + e.getMessage());
			// 合并失败，则清空进行重新上传
			this.fileManageService.deleteFileByIdentifier(ConvertUtil.convertToString(dataMap.get("identifier")));
			// 清空预签名
			this.redisTemplate.delete(ConvertUtil.convertToString(dataMap.get("identifier")));
			throw new ServiceException("上传失败，请重新上传");
		}

	}

	/**
	 * 处理视频块，进行切片转换
	 * 
	 * @description:
	 * @author: donghang
	 * @date: 2021年3月30日下午10:56:18
	 * @modify:
	 */
	public void processVideoBlock(InputStream inputStream, Map<String, Object> dataMap) {
		// 文件夹路径
		try {
			StringBuilder fileDirPath = new StringBuilder();
			fileDirPath.append(fileProperties.getStorePath()).append("/")
					.append(ConvertUtil.convertToString(dataMap.get("identifier")));

			String filePath = this.saveBlockToLocation(inputStream, dataMap);
			// ffmpeg视频切片
			FfmpegFileVideoConvert.getInstance().videoToImage(filePath, fileDirPath.toString(),
					ConvertUtil.convertToString(dataMap.get("identifier")));
			// 保存处理文件封面
			dataMap.put("foldername", ConvertUtil.convertToString(dataMap.get("filedirpath")));
			this.fileManageService.saveFileTitlePage(dataMap);
		} catch (Exception e) {
			log.error("处理视频封面异常:" + ConvertUtil.convertToString(dataMap.get("identifier")) + e.getMessage());
		}
	}

	/**
	 * 保存块文件到本地
	 * 
	 * @description:
	 * @return
	 * @author: donghang
	 * @throws IOException
	 * @date: 2021年4月2日下午5:33:55
	 * @modify:
	 */
	public String saveBlockToLocation(InputStream inputStream, Map<String, Object> dataMap) throws IOException {
		StringBuilder fileDirPath = new StringBuilder();
		fileDirPath.append(fileProperties.getStorePath()).append("/")
				.append(ConvertUtil.convertToString(dataMap.get("identifier")));
		// 判断uploadFolder/identifier 路径是否存在，不存在则创建
		if (!Files.isWritable(Paths.get(fileDirPath.toString()))) {
			Files.createDirectories(Paths.get(fileDirPath.toString()));
		}
		// 文件写入
		String blockFileName = ConvertUtil.convertToString(dataMap.get("filename"));
		int dotIndex = blockFileName.lastIndexOf(".");
		if (dotIndex == -1) {
			dotIndex = blockFileName.length();
		}
		String suffix = blockFileName.substring(dotIndex);
		blockFileName = blockFileName.substring(0, dotIndex);
		String filePath = fileDirPath.append("/")
				.append(blockFileName + "block-" + ConvertUtil.convertToString(dataMap.get("blocknum"))).append(suffix)
				.toString();
		Path path = Paths.get(filePath);
		if (Files.exists(path)) {
			Files.delete(path);
		}
		File blockFile = new File(filePath);
		blockFile.createNewFile();
		inputStream.reset();
		FileUtils.copyInputStreamToFile(inputStream, blockFile);
		return filePath;
	}
}
